/*
 * Binance Spot WebSocket API
 *
 * OpenAPI Specifications for the Binance Spot WebSocket API
 *
 * API documents:
 * - [Github web-socket-api documentation file](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md)
 * - [General API information for web-socket-api on website](https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api/general-api-information)
 *
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use anyhow::Context;
use async_trait::async_trait;
use derive_builder::Builder;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{collections::BTreeMap, sync::Arc};

use crate::common::{
    errors::WebsocketError,
    models::{ParamBuildError, WebsocketApiResponse},
    utils::remove_empty_value,
    websocket::{WebsocketApi, WebsocketMessageSendOptions},
};
use crate::spot::websocket_api::models;

#[async_trait]
pub trait AccountApi: Send + Sync {
    async fn account_commission(
        &self,
        params: AccountCommissionParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountCommissionResponseResult>>>;
    async fn account_rate_limits_orders(
        &self,
        params: AccountRateLimitsOrdersParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AccountRateLimitsOrdersResponseResultInner>>>;
    async fn account_status(
        &self,
        params: AccountStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountStatusResponseResult>>>;
    async fn all_order_lists(
        &self,
        params: AllOrderListsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrderListsResponseResultInner>>>;
    async fn all_orders(
        &self,
        params: AllOrdersParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrdersResponseResultInner>>>;
    async fn my_allocations(
        &self,
        params: MyAllocationsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyAllocationsResponseResultInner>>>;
    async fn my_prevented_matches(
        &self,
        params: MyPreventedMatchesParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyPreventedMatchesResponseResultInner>>>;
    async fn my_trades(
        &self,
        params: MyTradesParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyTradesResponseResultInner>>>;
    async fn open_order_lists_status(
        &self,
        params: OpenOrderListsStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrderListsStatusResponseResultInner>>>;
    async fn open_orders_status(
        &self,
        params: OpenOrdersStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersStatusResponseResultInner>>>;
    async fn order_amendments(
        &self,
        params: OrderAmendmentsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OrderAmendmentsResponseResultInner>>>;
    async fn order_list_status(
        &self,
        params: OrderListStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AllOrderListsResponseResultInner>>>;
    async fn order_status(
        &self,
        params: OrderStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderStatusResponseResult>>>;
}

#[derive(Clone)]
pub struct AccountApiClient {
    websocket_api_base: Arc<WebsocketApi>,
}

impl AccountApiClient {
    pub fn new(websocket_api_base: Arc<WebsocketApi>) -> Self {
        Self { websocket_api_base }
    }
}

/// Request parameters for the [`account_commission`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`account_commission`](#method.account_commission).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountCommissionParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
}

impl AccountCommissionParams {
    /// Create a builder for [`account_commission`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> AccountCommissionParamsBuilder {
        AccountCommissionParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`account_rate_limits_orders`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`account_rate_limits_orders`](#method.account_rate_limits_orders).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountRateLimitsOrdersParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AccountRateLimitsOrdersParams {
    /// Create a builder for [`account_rate_limits_orders`].
    ///
    #[must_use]
    pub fn builder() -> AccountRateLimitsOrdersParamsBuilder {
        AccountRateLimitsOrdersParamsBuilder::default()
    }
}
/// Request parameters for the [`account_status`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`account_status`](#method.account_status).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountStatusParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// When set to `true`, emits only the non-zero balances of an account. <br>Default value: false
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub omit_zero_balances: Option<bool>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AccountStatusParams {
    /// Create a builder for [`account_status`].
    ///
    #[must_use]
    pub fn builder() -> AccountStatusParamsBuilder {
        AccountStatusParamsBuilder::default()
    }
}
/// Request parameters for the [`all_order_lists`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`all_order_lists`](#method.all_order_lists).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AllOrderListsParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Aggregate trade ID to begin at
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_id: Option<i32>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AllOrderListsParams {
    /// Create a builder for [`all_order_lists`].
    ///
    #[must_use]
    pub fn builder() -> AllOrderListsParamsBuilder {
        AllOrderListsParamsBuilder::default()
    }
}
/// Request parameters for the [`all_orders`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`all_orders`](#method.all_orders).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AllOrdersParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AllOrdersParams {
    /// Create a builder for [`all_orders`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> AllOrdersParamsBuilder {
        AllOrdersParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`my_allocations`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`my_allocations`](#method.my_allocations).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct MyAllocationsParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    ///
    /// The `from_allocation_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_allocation_id: Option<i32>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl MyAllocationsParams {
    /// Create a builder for [`my_allocations`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> MyAllocationsParamsBuilder {
        MyAllocationsParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`my_prevented_matches`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`my_prevented_matches`](#method.my_prevented_matches).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct MyPreventedMatchesParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `prevented_match_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub prevented_match_id: Option<i64>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    ///
    /// The `from_prevented_match_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_prevented_match_id: Option<i64>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl MyPreventedMatchesParams {
    /// Create a builder for [`my_prevented_matches`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> MyPreventedMatchesParamsBuilder {
        MyPreventedMatchesParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`my_trades`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`my_trades`](#method.my_trades).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct MyTradesParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Aggregate trade ID to begin at
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_id: Option<i32>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl MyTradesParams {
    /// Create a builder for [`my_trades`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> MyTradesParamsBuilder {
        MyTradesParamsBuilder::default().symbol(symbol)
    }
}
/// Request parameters for the [`open_order_lists_status`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`open_order_lists_status`](#method.open_order_lists_status).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OpenOrderListsStatusParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OpenOrderListsStatusParams {
    /// Create a builder for [`open_order_lists_status`].
    ///
    #[must_use]
    pub fn builder() -> OpenOrderListsStatusParamsBuilder {
        OpenOrderListsStatusParamsBuilder::default()
    }
}
/// Request parameters for the [`open_orders_status`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`open_orders_status`](#method.open_orders_status).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OpenOrdersStatusParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// Describe a single symbol
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub symbol: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OpenOrdersStatusParams {
    /// Create a builder for [`open_orders_status`].
    ///
    #[must_use]
    pub fn builder() -> OpenOrdersStatusParamsBuilder {
        OpenOrdersStatusParamsBuilder::default()
    }
}
/// Request parameters for the [`order_amendments`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_amendments`](#method.order_amendments).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderAmendmentsParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    ///
    /// The `order_id` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub order_id: i64,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    ///
    /// The `from_execution_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_execution_id: Option<i64>,
    /// Default: 100; Maximum: 5000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderAmendmentsParams {
    /// Create a builder for [`order_amendments`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    /// * `order_id` — i64
    ///
    #[must_use]
    pub fn builder(symbol: String, order_id: i64) -> OrderAmendmentsParamsBuilder {
        OrderAmendmentsParamsBuilder::default()
            .symbol(symbol)
            .order_id(order_id)
    }
}
/// Request parameters for the [`order_list_status`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_list_status`](#method.order_list_status).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderListStatusParams {
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub orig_client_order_id: Option<String>,
    /// Cancel order list by orderListId
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_list_id: Option<i32>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderListStatusParams {
    /// Create a builder for [`order_list_status`].
    ///
    #[must_use]
    pub fn builder() -> OrderListStatusParamsBuilder {
        OrderListStatusParamsBuilder::default()
    }
}
/// Request parameters for the [`order_status`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`order_status`](#method.order_status).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct OrderStatusParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub symbol: String,
    /// Unique WebSocket request ID.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub id: Option<String>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_id: Option<i64>,
    /// `orderId`or`origClientOrderId`mustbesent
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub orig_client_order_id: Option<String>,
    /// The value cannot be greater than `60000`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl OrderStatusParams {
    /// Create a builder for [`order_status`].
    ///
    /// Required parameters:
    ///
    /// * `symbol` — String
    ///
    #[must_use]
    pub fn builder(symbol: String) -> OrderStatusParamsBuilder {
        OrderStatusParamsBuilder::default().symbol(symbol)
    }
}

#[async_trait]
impl AccountApi for AccountApiClient {
    async fn account_commission(
        &self,
        params: AccountCommissionParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountCommissionResponseResult>>> {
        let AccountCommissionParams { symbol, id } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::AccountCommissionResponseResult>>(
                "/account.commission".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn account_rate_limits_orders(
        &self,
        params: AccountRateLimitsOrdersParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AccountRateLimitsOrdersResponseResultInner>>>
    {
        let AccountRateLimitsOrdersParams { id, recv_window } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::AccountRateLimitsOrdersResponseResultInner>>(
                "/account.rateLimits.orders".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn account_status(
        &self,
        params: AccountStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AccountStatusResponseResult>>> {
        let AccountStatusParams {
            id,
            omit_zero_balances,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = omit_zero_balances {
            payload.insert("omitZeroBalances".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::AccountStatusResponseResult>>(
                "/account.status".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn all_order_lists(
        &self,
        params: AllOrderListsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrderListsResponseResultInner>>> {
        let AllOrderListsParams {
            id,
            from_id,
            start_time,
            end_time,
            limit,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = from_id {
            payload.insert("fromId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = start_time {
            payload.insert("startTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = end_time {
            payload.insert("endTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::AllOrderListsResponseResultInner>>(
                "/allOrderLists".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn all_orders(
        &self,
        params: AllOrdersParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::AllOrdersResponseResultInner>>> {
        let AllOrdersParams {
            symbol,
            id,
            order_id,
            start_time,
            end_time,
            limit,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = start_time {
            payload.insert("startTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = end_time {
            payload.insert("endTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::AllOrdersResponseResultInner>>(
                "/allOrders".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn my_allocations(
        &self,
        params: MyAllocationsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyAllocationsResponseResultInner>>> {
        let MyAllocationsParams {
            symbol,
            id,
            start_time,
            end_time,
            from_allocation_id,
            limit,
            order_id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = start_time {
            payload.insert("startTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = end_time {
            payload.insert("endTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = from_allocation_id {
            payload.insert("fromAllocationId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::MyAllocationsResponseResultInner>>(
                "/myAllocations".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn my_prevented_matches(
        &self,
        params: MyPreventedMatchesParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyPreventedMatchesResponseResultInner>>>
    {
        let MyPreventedMatchesParams {
            symbol,
            id,
            prevented_match_id,
            order_id,
            from_prevented_match_id,
            limit,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = prevented_match_id {
            payload.insert("preventedMatchId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = from_prevented_match_id {
            payload.insert("fromPreventedMatchId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::MyPreventedMatchesResponseResultInner>>(
                "/myPreventedMatches".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn my_trades(
        &self,
        params: MyTradesParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::MyTradesResponseResultInner>>> {
        let MyTradesParams {
            symbol,
            id,
            order_id,
            start_time,
            end_time,
            from_id,
            limit,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = start_time {
            payload.insert("startTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = end_time {
            payload.insert("endTime".to_string(), serde_json::json!(value));
        }
        if let Some(value) = from_id {
            payload.insert("fromId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::MyTradesResponseResultInner>>(
                "/myTrades".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn open_order_lists_status(
        &self,
        params: OpenOrderListsStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrderListsStatusResponseResultInner>>>
    {
        let OpenOrderListsStatusParams { id, recv_window } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::OpenOrderListsStatusResponseResultInner>>(
                "/openOrderLists.status".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn open_orders_status(
        &self,
        params: OpenOrdersStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OpenOrdersStatusResponseResultInner>>>
    {
        let OpenOrdersStatusParams {
            id,
            symbol,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = symbol {
            payload.insert("symbol".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::OpenOrdersStatusResponseResultInner>>(
                "/openOrders.status".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_amendments(
        &self,
        params: OrderAmendmentsParams,
    ) -> anyhow::Result<WebsocketApiResponse<Vec<models::OrderAmendmentsResponseResultInner>>> {
        let OrderAmendmentsParams {
            symbol,
            order_id,
            id,
            from_execution_id,
            limit,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        payload.insert("orderId".to_string(), serde_json::json!(order_id));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = from_execution_id {
            payload.insert("fromExecutionId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = limit {
            payload.insert("limit".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Vec<models::OrderAmendmentsResponseResultInner>>(
                "/order.amendments".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_list_status(
        &self,
        params: OrderListStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::AllOrderListsResponseResultInner>>> {
        let OrderListStatusParams {
            id,
            orig_client_order_id,
            order_list_id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = orig_client_order_id {
            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_list_id {
            payload.insert("orderListId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::AllOrderListsResponseResultInner>>(
                "/orderList.status".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }

    async fn order_status(
        &self,
        params: OrderStatusParams,
    ) -> anyhow::Result<WebsocketApiResponse<Box<models::OrderStatusResponseResult>>> {
        let OrderStatusParams {
            symbol,
            id,
            order_id,
            orig_client_order_id,
            recv_window,
        } = params;

        let mut payload: BTreeMap<String, Value> = BTreeMap::new();
        payload.insert("symbol".to_string(), serde_json::json!(symbol));
        if let Some(value) = id {
            payload.insert("id".to_string(), serde_json::json!(value));
        }
        if let Some(value) = order_id {
            payload.insert("orderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = orig_client_order_id {
            payload.insert("origClientOrderId".to_string(), serde_json::json!(value));
        }
        if let Some(value) = recv_window {
            payload.insert("recvWindow".to_string(), serde_json::json!(value));
        }
        let payload = remove_empty_value(payload);

        self.websocket_api_base
            .send_message::<Box<models::OrderStatusResponseResult>>(
                "/order.status".trim_start_matches('/'),
                payload,
                WebsocketMessageSendOptions::new().signed(),
            )
            .await
            .map_err(anyhow::Error::from)?
            .into_iter()
            .next()
            .ok_or(WebsocketError::NoResponse)
            .map_err(anyhow::Error::from)
    }
}

#[cfg(all(test, feature = "spot"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::common::websocket::{WebsocketApi, WebsocketConnection, WebsocketHandler};
    use crate::config::ConfigurationWebsocketApi;
    use crate::errors::WebsocketError;
    use crate::models::WebsocketApiRateLimit;
    use serde_json::{Value, json};
    use tokio::spawn;
    use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
    use tokio::time::{Duration, timeout};
    use tokio_tungstenite::tungstenite::Message;

    async fn setup() -> (
        Arc<WebsocketApi>,
        Arc<WebsocketConnection>,
        UnboundedReceiver<Message>,
    ) {
        let conn = WebsocketConnection::new("test-conn");
        let (tx, rx) = unbounded_channel::<Message>();
        {
            let mut conn_state = conn.state.lock().await;
            conn_state.ws_write_tx = Some(tx);
        }

        let config = ConfigurationWebsocketApi::builder()
            .api_key("key")
            .api_secret("secret")
            .build()
            .expect("Failed to build configuration");
        let ws_api = WebsocketApi::new(config, vec![conn.clone()]);
        conn.set_handler(ws_api.clone() as Arc<dyn WebsocketHandler>)
            .await;
        ws_api.clone().connect().await.unwrap();

        (ws_api, conn, rx)
    }

    #[test]
    fn account_commission_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountCommissionParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.account_commission(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/account.commission".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"d3df8a61-98ea-4fe0-8f4e-0fcea5d418b0","status":200,"result":{"symbol":"BTCUSDT","standardCommission":{"maker":"0.00000010","taker":"0.00000020","buyer":"0.00000030","seller":"0.00000040"},"specialCommission":{"maker":"0.01000000","taker":"0.02000000","buyer":"0.03000000","seller":"0.04000000"},"taxCommission":{"maker":"0.00000112","taker":"0.00000114","buyer":"0.00000118","seller":"0.00000116"},"discount":{"enabledForAccount":true,"enabledForSymbol":true,"discountAsset":"BNB","discount":"0.75000000"}},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::AccountCommissionResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn account_commission_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = AccountCommissionParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.account_commission(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn account_commission_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountCommissionParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.account_commission(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn account_rate_limits_orders_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
                client.account_rate_limits_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/account.rateLimits.orders".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"d3783d8d-f8d1-4d2c-b8a0-b7596af5a664","status":200,"result":[{"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":160000,"count":0},{"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":50,"count":0}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":40}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::AccountRateLimitsOrdersResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn account_rate_limits_orders_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
                client.account_rate_limits_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn account_rate_limits_orders_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountRateLimitsOrdersParams::builder().build().unwrap();
                client.account_rate_limits_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn account_status_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountStatusParams::builder().build().unwrap();
                client.account_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/account.status".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"605a6d20-6588-4cb9-afa0-b0ab087507ba","status":200,"result":{"makerCommission":15,"takerCommission":15,"buyerCommission":0,"sellerCommission":0,"canTrade":true,"canWithdraw":true,"canDeposit":true,"commissionRates":{"maker":"0.00150000","taker":"0.00150000","buyer":"0.00000000","seller":"0.00000000"},"brokered":false,"requireSelfTradePrevention":false,"preventSor":false,"updateTime":1660801833000,"accountType":"SPOT","balances":[{"asset":"USDT","free":"1021.21000000","locked":"0.00000000"},{"asset":"BTC","free":"1.3447112","locked":"0.08600000"},{"asset":"BNB","free":"0.00000000","locked":"0.00000000"}],"permissions":["SPOT"],"uid":354937868},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::AccountStatusResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn account_status_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = AccountStatusParams::builder().build().unwrap();
                client.account_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn account_status_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AccountStatusParams::builder().build().unwrap();
                client.account_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn all_order_lists_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AllOrderListsParams::builder().build().unwrap();
                client.all_order_lists(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/allOrderLists".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"8617b7b3-1b3d-4dec-94cd-eefd929b8ceb","status":200,"result":[{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}]}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::AllOrderListsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn all_order_lists_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = AllOrderListsParams::builder().build().unwrap();
                client.all_order_lists(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn all_order_lists_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AllOrderListsParams::builder().build().unwrap();
                client.all_order_lists(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn all_orders_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AllOrdersParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.all_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/allOrders".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"734235c2-13d2-4574-be68-723e818c08f3","status":200,"result":[{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00847000","cummulativeQuoteQty":"198.33521500","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"origQuoteOrderQty":"0.00000000","selfTradePreventionMode":"NONE","preventedMatchId":0,"preventedQuantity":"1.200000"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::AllOrdersResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn all_orders_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = AllOrdersParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.all_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn all_orders_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = AllOrdersParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.all_orders(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn my_allocations_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyAllocationsParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_allocations(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/myAllocations".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"g4ce6a53-a39d-4f71-823b-4ab5r391d6y8","status":200,"result":[{"symbol":"BTCUSDT","allocationId":0,"allocationType":"SOR","orderId":500,"orderListId":-1,"price":"1.00000000","qty":"0.10000000","quoteQty":"0.10000000","commission":"0.00000000","commissionAsset":"BTC","time":1687319487614,"isBuyer":false,"isMaker":false,"isAllocator":false}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::MyAllocationsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn my_allocations_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = MyAllocationsParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_allocations(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn my_allocations_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyAllocationsParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.my_allocations(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn my_prevented_matches_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_prevented_matches(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/myPreventedMatches".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"g4ce6a53-a39d-4f71-823b-4ab5r391d6y8","status":200,"result":[{"symbol":"BTCUSDT","preventedMatchId":1,"takerOrderId":5,"makerSymbol":"BTCUSDT","makerOrderId":3,"tradeGroupId":1,"selfTradePreventionMode":"EXPIRE_MAKER","price":"1.100000","makerPreventedQuantity":"1.300000","transactTime":1669101687094}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::MyPreventedMatchesResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn my_prevented_matches_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_prevented_matches(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn my_prevented_matches_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyPreventedMatchesParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.my_prevented_matches(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn my_trades_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyTradesParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_trades(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/myTrades".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"f4ce6a53-a29d-4f70-823b-4ab59391d6e8","status":200,"result":[{"symbol":"BTCUSDT","id":1650422482,"orderId":12569099453,"orderListId":-1,"price":"23416.50000000","qty":"0.00212000","quoteQty":"49.64298000","commission":"0.00000000","commissionAsset":"BNB","time":1660801715793,"isBuyer":false,"isMaker":true,"isBestMatch":true},{"symbol":"BTCUSDT","id":1650422481,"orderId":12569099453,"orderListId":-1,"price":"23416.10000000","qty":"0.00635000","quoteQty":"148.69223500","commission":"0.00000000","commissionAsset":"BNB","time":1660801715793,"isBuyer":false,"isMaker":true,"isBestMatch":true}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":20}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::MyTradesResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn my_trades_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = MyTradesParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.my_trades(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn my_trades_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = MyTradesParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.my_trades(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn open_order_lists_status_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrderListsStatusParams::builder().build().unwrap();
                client.open_order_lists_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/openOrderLists.status".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"3a4437e2-41a3-4c19-897c-9cadc5dce8b6","status":200,"result":[{"orderListId":0,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":5,"clientOrderId":"1ZqG7bBuYwaF4SU8CwnwHm"},{"symbol":"BTCUSDT","orderId":4,"clientOrderId":"CUhLgTXnX5n2c0gWiLpV4d"}]}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":6}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::OpenOrderListsStatusResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn open_order_lists_status_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OpenOrderListsStatusParams::builder().build().unwrap();
                client.open_order_lists_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn open_order_lists_status_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrderListsStatusParams::builder().build().unwrap();
                client.open_order_lists_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn open_orders_status_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrdersStatusParams::builder().build().unwrap();
                client.open_orders_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/openOrders.status".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"55f07876-4f6f-4c47-87dc-43e5fff3f2e7","status":200,"result":[{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00720000","origQuoteOrderQty":"0.00000000","cummulativeQuoteQty":"172.43931000","status":"PARTIALLY_FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"selfTradePreventionMode":"NONE"}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":6}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::OpenOrdersStatusResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn open_orders_status_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OpenOrdersStatusParams::builder().build().unwrap();
                client.open_orders_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn open_orders_status_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OpenOrdersStatusParams::builder().build().unwrap();
                client.open_orders_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_amendments_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(),1,).build().unwrap();
                client.order_amendments(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.amendments".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"6f5ebe91-01d9-43ac-be99-57cf062e0e30","status":200,"result":[{"symbol":"BTCUSDT","orderId":23,"executionId":60,"origClientOrderId":"my_pending_order","newClientOrderId":"xbxXh5SSwaHS7oUEOCI88B","origQty":"7.00000000","newQty":"5.00000000","time":1741924229819}],"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Vec<models::OrderAmendmentsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_amendments_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(),1,).build().unwrap();
                client.order_amendments(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_amendments_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderAmendmentsParams::builder("BNBUSDT".to_string(), 1)
                    .build()
                    .unwrap();
                client.order_amendments(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_list_status_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListStatusParams::builder().build().unwrap();
                client.order_list_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/orderList.status".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"b53fd5ff-82c7-4a04-bd64-5f9dc42c2100","status":200,"result":{"orderListId":1274512,"contingencyType":"OCO","listStatusType":"EXEC_STARTED","listOrderStatus":"EXECUTING","listClientOrderId":"08985fedd9ea2cf6b28996","transactionTime":1660801713793,"symbol":"BTCUSDT","orders":[{"symbol":"BTCUSDT","orderId":12569138902,"clientOrderId":"jLnZpj5enfMXTuhKB1d0us"},{"symbol":"BTCUSDT","orderId":12569138901,"clientOrderId":"BqtFCj5odMoWtSqGk2X9tU"}]},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::AllOrderListsResponseResultInner> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_list_status_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderListStatusParams::builder().build().unwrap();
                client.order_list_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_list_status_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderListStatusParams::builder().build().unwrap();
                client.order_list_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }

    #[test]
    fn order_status_success() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderStatusParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.expect("send should occur").expect("channel closed");
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap();
            assert_eq!(v["method"], "/order.status".trim_start_matches('/'));

            let mut resp_json: Value = serde_json::from_str(r#"{"id":"aa62318a-5a97-4f3b-bdc7-640bbe33b291","status":200,"result":{"symbol":"BTCUSDT","orderId":12569099453,"orderListId":-1,"clientOrderId":"4d96324ff9d44481926157","price":"23416.10000000","origQty":"0.00847000","executedQty":"0.00847000","cummulativeQuoteQty":"198.33521500","status":"FILLED","timeInForce":"GTC","type":"LIMIT","side":"SELL","stopPrice":"0.00000000","trailingDelta":10,"trailingTime":-1,"icebergQty":"0.00000000","time":1660801715639,"updateTime":1660801717945,"isWorking":true,"workingTime":1660801715639,"origQuoteOrderQty":"0.00000000","strategyId":37463720,"strategyType":1000000,"selfTradePreventionMode":"NONE","preventedMatchId":0,"preventedQuantity":"1.200000"},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":4}]}"#).unwrap();
            resp_json["id"] = id.into();

            let raw_data = resp_json.get("result").or_else(|| resp_json.get("response")).expect("no response in JSON");
            let expected_data: Box<models::OrderStatusResponseResult> = serde_json::from_value(raw_data.clone()).expect("should parse raw response");
            let empty_array = Value::Array(vec![]);
            let raw_rate_limits = resp_json.get("rateLimits").unwrap_or(&empty_array);
            let expected_rate_limits: Option<Vec<WebsocketApiRateLimit>> =
                match raw_rate_limits.as_array() {
                    Some(arr) if arr.is_empty() => None,
                    Some(_) => Some(serde_json::from_value(raw_rate_limits.clone()).expect("should parse rateLimits array")),
                    None => None,
                };

            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let response = timeout(Duration::from_secs(1), handle).await.expect("task done").expect("no panic").expect("no error");


            let response_rate_limits = response.rate_limits.clone();
            let response_data = response.data().expect("deserialize data");

            assert_eq!(response_rate_limits, expected_rate_limits);
            assert_eq!(response_data, expected_data);
        });
    }

    #[test]
    fn order_status_error_response() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = tokio::spawn(async move {
                let params = OrderStatusParams::builder("BNBUSDT".to_string(),).build().unwrap();
                client.order_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv()).await.unwrap().unwrap();
            let Message::Text(text) = sent else { panic!() };
            let v: Value = serde_json::from_str(&text).unwrap();
            let id = v["id"].as_str().unwrap().to_string();

            let resp_json = json!({
                "id": id,
                "status": 400,
                    "error": {
                        "code": -2010,
                        "msg": "Account has insufficient balance for requested action.",
                    },
                    "rateLimits": [
                        {
                            "rateLimitType": "ORDERS",
                            "interval": "SECOND",
                            "intervalNum": 10,
                            "limit": 50,
                            "count": 13
                        },
                    ],
            });
            WebsocketHandler::on_message(&*ws_api, resp_json.to_string(), conn.clone()).await;

            let join = timeout(Duration::from_secs(1), handle).await.unwrap();
            match join {
                Ok(Err(e)) => {
                    let msg = e.to_string();
                    assert!(
                        msg.contains("Server‐side response error (code -2010): Account has insufficient balance for requested action."),
                        "Expected error msg to contain server error, got: {msg}"
                    );
                }
                Ok(Ok(_)) => panic!("Expected error"),
                Err(_) => panic!("Task panicked"),
            }
        });
    }

    #[test]
    fn order_status_request_timeout() {
        TOKIO_SHARED_RT.block_on(async {
            let (ws_api, _conn, mut rx) = setup().await;
            let client = AccountApiClient::new(ws_api.clone());

            let handle = spawn(async move {
                let params = OrderStatusParams::builder("BNBUSDT".to_string())
                    .build()
                    .unwrap();
                client.order_status(params).await
            });

            let sent = timeout(Duration::from_secs(1), rx.recv())
                .await
                .expect("send should occur")
                .expect("channel closed");
            let Message::Text(text) = sent else {
                panic!("expected Message Text")
            };

            let _: Value = serde_json::from_str(&text).unwrap();

            let result = handle.await.expect("task completed");
            match result {
                Err(e) => {
                    if let Some(inner) = e.downcast_ref::<WebsocketError>() {
                        assert!(matches!(inner, WebsocketError::Timeout));
                    } else {
                        panic!("Unexpected error type: {:?}", e);
                    }
                }
                Ok(_) => panic!("Expected timeout error"),
            }
        });
    }
}
